/**
 * This file is part of JNotifyOSD.
 *
 * JNotifyOSD is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JNotifyOSD is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with JNotifyOSD. If not, see <http://www.gnu.org/licenses/>.
 */

#include "stdafx.h"
#include <GdiPlus.h>
#include <errno.h>
#include "IcoLoader.h"

static ULONG_PTR gdiplusToken = NULL;

static Gdiplus::Status initializeGDI()
{
	Gdiplus::GdiplusStartupInput input;
	
	return Gdiplus::GdiplusStartup(&gdiplusToken, &input, NULL);
}

/**
 * Converts utf8 chars to unicode chars
 * The returned array must be delete[]d after use
 */
static WCHAR* ConvertToWChars(const char* orig)
{
	size_t orig_len = strlen(orig);
	size_t dst_len;
	WCHAR* dst = new WCHAR[orig_len + 1];
	switch (mbstowcs_s(&dst_len, dst, orig_len + 1, orig + (*orig == '/'), _TRUNCATE))
	{
	case EINVAL:
	case ERANGE:
		break;
	default:
		for (int i = 0; i < dst_len; ++i)
		{
			if (dst[i] == L'/')
			{
				dst[i] = L'\\';
			}
		}

		return dst;
	}
	
	delete [] dst;
	return NULL;
}

/**
 * Converts a Bitmap into a icon
 * The icon must be freed with DestroyIcon when it's not needed anymore.
 */
static int ConvertImage(Gdiplus::Bitmap *img, HICON *outIco)
{
	unsigned int aSize = img->GetWidth();
	aSize *= aSize;

	BYTE *andMask = new BYTE[(aSize + 7) / 8];	//1/8 Bytes per bit
	BYTE *xorMask = new BYTE[aSize * 4];		//4 Bytes per pixel

	DWORD *vals = reinterpret_cast<DWORD*>(xorMask);
	ZeroMemory(andMask, (aSize + 7) / 8 * sizeof(BYTE));

	Gdiplus::Color tc(0xff, 0, 0);
	UINT rowLen = img->GetWidth();
	UINT colLen = img->GetHeight();
	for (UINT y = 0; y < colLen; ++y)
	{
		for (UINT x = 0; x < rowLen; ++x)
		{
			img->GetPixel(x, y, &tc);
			vals[y * rowLen + x] = tc.GetValue();
		}
	}

	*outIco = CreateIcon(NULL, rowLen, colLen, 1, 32, andMask, xorMask);

	if (*outIco == NULL)
	{
		DWORD err = GetLastError();
		DWORD px = err;
		delete [] andMask;
		delete [] xorMask;

		return 1;
	}

	delete [] andMask;
	delete [] xorMask;

	return 0;
}

int IconFromImageFile(const char* path, HICON* outIco)
{
	WCHAR* wPath = ConvertToWChars(path);
	if (wPath == 0)
	{
		return 1;
	}
	
	int res = IconFromImageFile(wPath, outIco);
	delete [] wPath;
	return res;
}

int IconFromImageFile(const WCHAR* path, HICON* outIco)
{
	//TODO use meaningful error codes

	if (gdiplusToken == NULL && initializeGDI() != Gdiplus::Status::Ok)
	{
		return 1;
	}

	Gdiplus::Bitmap* img = Gdiplus::Bitmap::FromFile(path);
	if (img == NULL)
	{
		return 1;
	}
	
	int res = 1;

	//Check size is a power of 2
	static const unsigned short PWR2[] = {8, 16, 32, 64, 128, 256};
	for (int i = sizeof(PWR2) / sizeof(unsigned short) - 1; i >= 0; --i)
	{
		if (img->GetWidth() == PWR2[i] && img->GetHeight() == PWR2[i])
		{
			res = ConvertImage(img, outIco);
		}
	}

	delete img;
	return res;
}

void FreeIcoLoaderMemory()
{
	if (gdiplusToken != NULL)
	{
		Gdiplus::GdiplusShutdown(gdiplusToken);
		gdiplusToken = NULL;
	}
}